package cn.raven.basic.service.impl;

import cn.raven.basic.constant.BaseConstant;
import cn.raven.basic.dto.LoginDto;
import cn.raven.basic.dto.PhoneLoginDto;
import cn.raven.basic.dto.VerifyRegisterDto;
import cn.raven.basic.exception.BusinessException;
import cn.raven.basic.jwt.JwtUtils;
import cn.raven.basic.jwt.RsaUtils;
import cn.raven.basic.jwt.UserJwt;
import cn.raven.basic.service.ILoginService;
import cn.raven.basic.utils.AjaxResult;
import cn.raven.basic.utils.HttpUtil;
import cn.raven.basic.utils.MD5Utils;
import cn.raven.basic.utils.StrUtils;
import cn.raven.system.domain.Menu;
import cn.raven.system.mapper.MenuMapper;
import cn.raven.system.mapper.PermissionMapper;
import cn.raven.user.domain.Logininfo;
import cn.raven.user.domain.User;
import cn.raven.user.domain.Wxuser;
import cn.raven.user.dto.WxDto;
import cn.raven.user.mapper.LogininfoMapper;
import cn.raven.user.mapper.UserMapper;
import cn.raven.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonParser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Value("${jwt.rsa.pri}")
    private String jwtRsaPri;

    /**
     * @Title: account
     * @Description: 前台用户账号登录
     * @Author: king
     * @Version: 1.0
     * @Date: 2022/7/8 12:00
     * @Parameters: [loginDto]
     * @Return java.util.Map<java.lang.String, java.lang.Object>
     */
    @Override
    public Map<String, Object> account(LoginDto loginDto) {
        String username = loginDto.getUsername();
        String password = loginDto.getPassword();
        String type = loginDto.getType();
        //非空校验
        if (StringUtils.isEmpty(username) ||
                StringUtils.isEmpty(password) ||
                StringUtils.isEmpty(type)) {
            throw new BusinessException("参数不能为空");
        }
        //通过username和type查看是否存在
        Logininfo logininfo = logininfoMapper.findByUsernameAndType(username, type);
        if (logininfo == null) {
            throw new BusinessException("账号或密码G了");
        }
        //将用户填写的密码进行加盐加密，根据数据库中的加盐加密进行比较
        String saltPassword = MD5Utils.encrypByMd5(logininfo.getSalt() + password);
        if (!logininfo.getPassword().equals(saltPassword)) {
            throw new BusinessException("账号或密码错误");
        }
        return jwtToken(logininfo);
    }


    public Map<String,Object> jwtToken(Logininfo logininfo){
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(jwtRsaPri).getFile());
        UserJwt userJwt = new UserJwt();

        Map<String, Object> map = new HashMap<>();
        //表明是后端
        if (logininfo.getType() == 0){
            List<Menu> menus = menuMapper.findOwnMenuByLogininfoId(logininfo.getId());
            List<String> permissions = permissionMapper.findOwnPermissionByLogininfoId(logininfo.getId());
            userJwt.setMenus(menus);
            userJwt.setPermissions(permissions);
            map.put("menus",menus);
            map.put("permissions",permissions);
        }
        userJwt.setLogininfo(logininfo);
        String token = JwtUtils.generateTokenExpireInMinutes(userJwt, privateKey, 60*8);
        /*//生成uuid
        String uuid = UUID.randomUUID().toString();
        //保存redis
        redisTemplate.opsForValue().set(uuid, logininfo, 30, TimeUnit.MINUTES);
        //敏感信息置为空*/
        logininfo.setSalt("");
        logininfo.setPassword("");
        //保存到map传递给前端

        map.put("token", token);
        map.put("logininfo", logininfo);
        return map;
    }
    /**
     * @Title: phoneCode
     * @Description: 手机登录验证码
     * @Author: king
     * @Version: 1.0
     * @Date: 2022/7/8 13:28
     * @Parameters: [phoneLoginDto]
     * @Return void
     */
    @Override
    public void phoneCode(PhoneLoginDto phoneLoginDto) {
        String phone = phoneLoginDto.getPhone();
        String type = phoneLoginDto.getType();
        String imageCodeKey = phoneLoginDto.getImageCodeKey();
        String imageCodeValue = phoneLoginDto.getImageCodeValue();
        if (StringUtils.isEmpty(phone)) {
            throw new BusinessException("参数不能为空!");
        }

        Logininfo logininfo = logininfoMapper.findByPhoneAndType(phone,type);
        if (logininfo == null) {
            throw new BusinessException("电话错误!");
        }
        //判断图形验证码是否输入正确
        Object imageCodeValueRedis = redisTemplate.opsForValue().get(imageCodeKey);
        if (imageCodeValueRedis == null){
            throw new BusinessException("验证码已过期!");
        }
        if (!imageCodeValueRedis.toString().equalsIgnoreCase(imageCodeValue)){
            throw new BusinessException("验证码错误!");
        }

        //判断短信验证码是否存在
        String phonekey = BaseConstant.BusinessConstant.SMS_PHONE_LOGIN_CODE + phone;
        Object phoneCodeValue = redisTemplate.opsForValue().get(phonekey);
        String code = "";
        if (phoneCodeValue == null) {
            //如果redis没有验证码
            code = StrUtils.getComplexRandomString(4);
        } else {//如果有
            String[] smsSplit = phoneCodeValue.toString().split(":");
            Long oldTime = Long.valueOf(smsSplit[0]);
            Long nowTime = System.currentTimeMillis();
            //当前时间戳减去存在验证码的时间戳，小于一分钟
            if (nowTime - oldTime < 1 * 60 * 1000) {
                throw new BusinessException("请一分钟后点击获取");
            }
            //直接使用当前验证码
            code = smsSplit[1];
        }
        //设置时间戳+验证码
        String codeRedis = System.currentTimeMillis()+":"+code;
        redisTemplate.opsForValue().set(phonekey,codeRedis,3,TimeUnit.MINUTES);
        System.out.println("你的验证码是："+ code);
    }
    /** 
    * @Title: phonein
    * @Description: 手机号登录
    * @Author: king
    * @Version: 1.0
    * @Date:  2022/7/9 16:14  
    * @Parameters: [phoneLoginDto]
    * @Return java.util.Map<java.lang.String,java.lang.Object>
      */
    @Override
    public Map<String, Object> phonein(PhoneLoginDto phoneLoginDto) {
        String phone = phoneLoginDto.getPhone();
        String phoneCode = phoneLoginDto.getPhoneCode();
        String imageCodeKey = phoneLoginDto.getImageCodeKey();
        String imageCodeValue = phoneLoginDto.getImageCodeValue();
        String type = phoneLoginDto.getType();
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(phoneCode) || StringUtils.isEmpty(type)) {
            throw new BusinessException("参数不能为空!");
        }
        //判断电话号是否存在
        Logininfo logininfo = logininfoMapper.findByPhoneAndType(phone,type);
        if (logininfo == null) {
            throw new BusinessException("电话错误!");
        }
        //判断图形验证码是否输入正确
        Object imageCodeValueRedis = redisTemplate.opsForValue().get(imageCodeKey);
        if (imageCodeValueRedis == null){
            throw new BusinessException("验证码已过期!");
        }
        if (!imageCodeValueRedis.toString().equalsIgnoreCase(imageCodeValue)){
            throw new BusinessException("验证码错误!");
        }
        //根据业务键和电话为key去Redis中找验证码
        String phonekey = BaseConstant.BusinessConstant.SMS_PHONE_LOGIN_CODE + phone;
        Object phoneCodeValue = redisTemplate.opsForValue().get(phonekey);
        if (phoneCodeValue == null){
            throw new BusinessException("验证码已过期");
        }
        //有值就根据 ： 分割进行判断用户输入的和Redis保存的是否一致
        String phoneCodeRedis = phoneCodeValue.toString().split(":")[1];
        if (!phoneCodeRedis.equalsIgnoreCase(phoneCode)){
            throw new BusinessException("验证码错误");
        }
        return jwtToken(logininfo);
    }
    
    /** 
    * @Title: wechat
    * @Description: 微信扫码登录
    * @Author: king
    * @Version: 1.0
    * @Date:  2022/7/10 16:45  
    * @Parameters: [wxDto]
    * @Return cn.raven.basic.utils.AjaxResult
      */
    @Override
    public AjaxResult wechat(WxDto wxDto) {
        String code = wxDto.getCode();
        //非空校验
        if (StringUtils.isEmpty(wxDto)){
            throw new BusinessException("参数不能为空!");
        }
        //根据API获取access_token和openid
        String tokenUrl = BaseConstant.WxApi.SMS_ACCESS_TOKEN
                .replace("APPID", BaseConstant.WxApi.APPID)
                .replace("SECRET", BaseConstant.WxApi.SECRET)
                .replace("CODE", code);
        //根据url发起get请求获取json格式的字符串对象
        String jsonObjStr = HttpUtil.httpGet(tokenUrl);
        //将json格式的字符串对象转为json对象
        JSONObject jsonObj = JSONObject.parseObject(jsonObjStr);
        //根据官网的key获取值
        String accessToken = jsonObj.getString("access_token");
        String openid = jsonObj.getString("openid");
        //根据openid查询wxuser对象
        Wxuser wxuser = wxuserMapper.findByOpenId(openid);
        //判断wxuser是否存在并绑定账号
        if (wxuser != null && wxuser.getUserId() != null){//并判断userid是否存在
            //根据wxuser的UserId查询user表再根据user表的外键查询Logininfo获取logininfo对象
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            Map<String, Object> map = jwtToken(logininfo);
            return AjaxResult.me().setResultObj(map);
        }
        //不存在就将accessToken和openid拼接传递到前端绑定账号页面
        StringBuffer url = new StringBuffer()
                .append("?accessToken=")
                .append(accessToken)
                .append("&openId=")
                .append(openid);
        return AjaxResult.me().setSuccess(false).setMessage("nobinder").setResultObj(url);
    }

    /**
    * @Title: binder
    * @Description: 微信绑定
    * @Author: king
    * @Version: 1.0
    * @Date:  2022/7/10 17:31
    * @Parameters: [wxDto]
    * @Return java.util.Map<java.lang.String,java.lang.Object>
      */
    @Override
    public Map<String, Object> binder(WxDto wxDto) {
        String phone = wxDto.getPhone();
        String verifyCode = wxDto.getVerifyCode();
        String accessToken = wxDto.getAccessToken();
        String openId = wxDto.getOpenId();
        //非空校验
        if (StringUtils.isEmpty(phone) ||
                StringUtils.isEmpty(verifyCode) ||
                StringUtils.isEmpty(accessToken) ||
                StringUtils.isEmpty(openId)){
            throw new BusinessException("参数不能为空!");
        }
        //校验图形验证码
        String smsCodeKey= BaseConstant.BusinessConstant.SMS_BINDER_CODE+ phone;
        Object smsCode = redisTemplate.opsForValue().get(smsCodeKey);
        if (smsCode == null){
            throw new BusinessException("验证码已过期");
        }
        String codeRedis = smsCode.toString().split(":")[1];
        if (!codeRedis.equalsIgnoreCase(verifyCode)){
            throw new BusinessException("验证码错误!");
        }
        //根据手机号判断是否存在
        User user = userMapper.findByPhone(phone);
        Logininfo logininfo = null;
        if (user == null){
            //初始化user
            user = register2User(phone);
            //copy到logininfo
            logininfo = user2Logininfo(user);
            logininfoMapper.add(logininfo);
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        }else {
            logininfo = logininfoMapper.findByUserId(user.getId());
        }
        //拼接API
        String url = BaseConstant.WxApi.WXUSER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        //发起get请求
        String wxUserStr  = HttpUtil.httpGet(url);
        //给wxuser赋值
        Wxuser wxuser = JSONObject.parseObject(wxUserStr, Wxuser.class);
        //设置userid
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
       return jwtToken(logininfo);
    }
    
    /** 
    * @Title: register2User
    * @Description: 初始化user
    * @Author: king
    * @Version: 1.0
    * @Date:  2022/7/10 18:01  
    * @Parameters: [phone]
    * @Return cn.raven.user.domain.User
      */
    private User register2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        String initPassword = "1";
        String saltPassword = MD5Utils.encrypByMd5(salt + initPassword);
        user.setSalt(salt);
        user.setPassword(saltPassword);
        System.out.println("您的初始密码是："+ initPassword);
        return user;
    }
    /** 
    * @Title: user2Logininfo
    * @Description: 拷贝user到logininfo
    * @Author: king
    * @Version: 1.0
    * @Date:  2022/7/10 18:02  
    * @Parameters: [user]
    * @Return cn.raven.user.domain.Logininfo
      */
    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);
        logininfo.setId(user.getLogininfoId());
        return logininfo;
    }
}
